#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/poll.h>


#define BUFSIZE 128

char kbuf[BUFSIZE] = {0};
atomic_t my_atomic = ATOMIC_INIT(1);
wait_queue_head_t wq_head;
int wq_condition = 0;


#define ERR(msg) do {	\
	printk("err: %s\n", msg);	\
	printk("err_at: %s:%s %d\n", __FILE__, __func__, __LINE__);	\
} while(0)

ssize_t demo_read (struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{
	int ret = 0;
	printk("%s invoked\n", __func__);

	// 非阻塞读取数据立即返回
	if (file->f_flags == O_NONBLOCK) {
		ret = copy_to_user(ubuf, kbuf, BUFSIZE);

		if (ret < 0) {
			ERR("copy_to_user");
			return -ENOMEM;
		}
	
		return strlen(kbuf);
	}
	
	// 阻塞读取数据逻辑
	ret = wait_event_interruptible(wq_head, wq_condition);
	if (ret == -ERESTARTSYS) {
		printk("signal weak up.\n");
		return ret;
	}

	printk("this is demo read function.\n");
	printk("read block weak up successfuly.\n");

	wq_condition = 0;
	copy_to_user(ubuf, kbuf, BUFSIZE);
	memset(kbuf, 0, BUFSIZE);

	return strlen(kbuf);
}

ssize_t demo_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
	int ret = 0;
	printk("%s invoked\n", __func__);

	memset(kbuf, 0, BUFSIZE);
	size = size > BUFSIZE ? BUFSIZE : size;
	ret = copy_from_user(kbuf, ubuf, size);
	if (ret < 0) {
		ERR("copy_from_user");
		return -ENOMEM;
	}

	printk("demo write copy_from_user invoked.\n");
	printk("wake up read block.\n");
	
	wq_condition = 1;
	wake_up_interruptible(&wq_head);

	return size;
}

/**********************
 	POLLIN, 读事件
	POLLPRI, 读事件，但表示紧急数据，例如tcp socket的带外数据
	POLLRDNORM, 读事件，表示有普通数据可读　　　  
	POLLRDBAND,　读事件，表示有优先数据可读　　　　 
	POLLOUT，写事件
	POLLWRNORM , 写事件，表示有普通数据可写
	POLLWRBAND ,　写事件，表示有优先数据可写　　　   　　　　  
	POLLRDHUP (since Linux 2.6.17)，Stream socket的一端关闭了连接（注意是stream socket，我们知道还有raw socket,dgram socket），或者是写端关闭了连接，如果要使用这个事件，必须定义_GNU_SOURCE 宏。这个事件可以用来判断链路是否发生异常（当然更通用的方法是使用心跳机制）。要使用这个事件，得这样包含头文件：
	　　#define _GNU_SOURCE  
	　　#include <poll.h>
	POLLERR，仅用于内核设置传出参数revents，表示设备发生错误
	POLLHUP，仅用于内核设置传出参数revents，表示设备被挂起，如果poll监听的fd是socket，表示这个socket并没有在网络上建立连接，比如说只调用了socket()函数，但是没有进行connect。
	POLLNVAL，仅用于内核设置传出参数revents，表示非法请求文件描述符fd没有打开
**********************/
unsigned int demo_poll (struct file *filp, struct poll_table_struct *p)
{
	unsigned int mask = 0;

	poll_wait(filp, &wq_head, p);
	
	if (wq_condition) {
		printk("demo_poll function was invoked.\n");
		mask |= POLLIN;
	}

	return mask;
}


int demo_open (struct inode *inode, struct file *file)
{
	printk("%s invoked\n", __func__);

	return 0;
}

int demo_close (struct inode *inode, struct file *file)
{
	printk("%s invoked\n", __func__);

	return 0;
}


struct cdev *cdev_p;
int major = 666;
int minor = 0;
unsigned int dev_count = 1;
const char *device_name = "demo_dev";
struct class *cls_p = NULL;
struct device *dev_p = NULL;
struct file_operations fops = {
	.open 		= demo_open,
	.release 	= demo_close,
	.read 		= demo_read,
	.write 		= demo_write,
	.poll		= demo_poll,
};

static int __init demo_init(void)
{
	int ret = 0;
	dev_t devt = MKDEV(major, minor);

	printk("%s invoked\n", __func__);

	// ----- 分步注册字符设备驱动 -----
	// 1. 申请字符设备结构体
	cdev_p = cdev_alloc();
	if (NULL == cdev_p) {
		ERR("cdev_alloc");
		goto ERR1;
	}

	// 2. 初始化字符设备结构体
	cdev_init(cdev_p, &fops);

	// 3. 申请设备号
	ret = register_chrdev_region(devt, dev_count, device_name);
	if (ret < 0) {
		printk("errno: %d\n", ret);
		ERR("register_chrdev_region");
		goto ERR2;
	}

	// 4. 注册字符设备驱动
	ret = cdev_add(cdev_p, devt, dev_count);
	if (ret < 0) {
		ERR("cdev_add");
		goto ERR3;
	}

	// ----- 自动创建驱动设备节点 -----

	// 1. 创建设备目录
	cls_p = class_create(THIS_MODULE, device_name);
	if (IS_ERR(cls_p)) {
		ERR("class_create");
		goto ERR4;
	}
	
	// 2. 创建设备文件节点
	dev_p =  device_create(cls_p, NULL, devt, NULL, device_name);
	if (IS_ERR(dev_p)) {
		ERR("device_create");
		goto ERR5;
	}

	// 初始化阻塞IO等待队列头
	init_waitqueue_head(&wq_head);

	return 0;

ERR5:
	class_destroy(cls_p);
	
ERR4:
	cdev_del(cdev_p);
	
ERR3:
	unregister_chrdev_region(devt, dev_count);
	
ERR2:
	kfree(cdev_p);

ERR1:
	return ret;
}

static void __exit demo_exit(void)
{
	printk("%s invoked.\n", __func__);

	device_destroy(cls_p, MKDEV(major, minor));
	class_destroy(cls_p);
	cdev_del(cdev_p);
	unregister_chrdev_region(MKDEV(major, minor), dev_count);
	kfree(cdev_p);

	printk("%s end.\n", __func__);
	
	return ;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");


